
#include <BFile>
#include <BFolder>
#include <BImage>
#include <BReadWrite>
#include <BSystem>
#include "member_BStyleDocument.h"
#include "IHtmlStyleReader.h"

using namespace BWE;

bool BWE::StringToColor(const BString& text, BColor& color)
{
	if (text.empty() || text == "none")
	{
		color.set(0, 0, 0, 0);
		return true;
	}
	if (text.beginWith('#'))
	{
		const char* cstr = text.cstr() + 1;
		int size = text.size() - 1;
		if (size == 3)
		{
			BByte r = bHexValue(cstr[0]);
			BByte g = bHexValue(cstr[1]);
			BByte b = bHexValue(cstr[2]);
			color.r() = r * 16 + r;
			color.g() = g * 16 + g;
			color.b() = b * 16 + b;
			color.a() = 255;
			return true;
		}
		if (size == 6)
		{
			color.r() = bHexToInt(cstr + 0, 2);
			color.g() = bHexToInt(cstr + 2, 2);
			color.b() = bHexToInt(cstr + 4, 2);
			color.a() = 255;
			return true;
		}
		if (size == 8)
		{
			color.r() = bHexToInt(cstr + 0, 2);
			color.g() = bHexToInt(cstr + 2, 2);
			color.b() = bHexToInt(cstr + 4, 2);
			color.a() = bHexToInt(cstr + 6, 2);
			return true;
		}
		return false;
	}
	if (text.beginWith("rgb("))
	{
		int begin = text.find('(');
		int end = text.find(')');
		BString temp(text, begin + 1, end);
		BStringArray words;
		temp.split(words, ',');
		if (words.size() == 3)
		{
			color.r() = words[0].toInt();
			color.g() = words[1].toInt();
			color.b() = words[2].toInt();
			color.a() = 255;
			return true;
		}
		return false;
	}
	if (text.beginWith("rgba("))
	{
		int begin = text.find('(');
		int end = text.find(')');
		BString temp(text, begin + 1, end);
		BStringArray words;
		temp.split(words, ',');
		if (words.size() == 4)
		{
			color.r() = words[0].toInt();
			color.g() = words[1].toInt();
			color.b() = words[2].toInt();
			color.a() = words[3].toInt();
			return true;
		}
		return false;
	}
	if (text.contain(','))
	{
		BStringArray words;
		text.split(words, ',');
		if (words.size() < 3 || words.size() > 4)
			return false;
		color.r() = words[0].toInt();
		color.g() = words[1].toInt();
		color.b() = words[2].toInt();
		color.a() = 255;
		if (words.size() == 4)
			color.a() = words[3].toInt();
		return true;
	}
	BString temp = text;
	temp.lower();
	color = BSystem::NamedColor(temp);
	return color.a();
}
bool BWE::StringToAlign(const BString& text, Align& align)
{
	align = Align_None;
	if (text == "none")
		align = Align_None;
	else if (text == "center")
		align = Align_Center;
	else if (text == "left-center")
		align = Align_LeftCenter;
	else if (text == "right-center")
		align = Align_RightCenter;
	else if (text == "top-center")
		align = Align_TopCenter;
	else if (text == "bottom-center")
		align = Align_BottomCenter;
	else
	{
		BString word;
		int pos = 0;
		while (pos < text.size())
		{
			pos = text.word(word, pos);
			if (word == ',')
				continue;
			else if (word == "left")
				((int&)align) |= Align_Left;
			else if (word == "right")
				((int&)align) |= Align_Right;
			else if (word == "top")
				((int&)align) |= Align_Top;
			else if (word == "bottom")
				((int&)align) |= Align_Bottom;
			else if (word == "hcenter")
				((int&)align) |= Align_HCenter;
			else if (word == "vcenter")
				((int&)align) |= Align_VCenter;
		}
	}
	return true;
}
bool BWE::StringToForm(BString& text, Form& form)
{
	form = Form_None;
	if (text == "icon-alone")
		form = Form_IconAlone;
	else if (text == "text-alone")
		form = Form_TextAlone;
	else if (text == "text-only")
		form = Form_TextOnly;
	else if (text == "icon-only")
		form = Form_IconOnly;
	else if (text == "icon-text")
		form = Form_IconText;
	else if (text == "text-icon")
		form = Form_TextIcon;
	else if (text == "text-under-icon" || text == "icon-above-text")
		form = Form_TextUnderIcon;
	else if (text == "icon-under-text" || text == "text-above-icon")
		form = Form_IconUnderText;
	else
		return false;
	return true;
}
bool BWE::StringToPoint(BString& text, BPoint& point)
{
	if (!text.isNumber())
	{
		return false;
	}
	if (text.contain(','))
	{
		BStringArray words;
		text.split(words, ',');
		if (words.size() != 2)
			return false;
		point.x() = words[0].toInt();
		point.y() = words[1].toInt();
		return true;
	}
	int s = text.toInt();
	point.set(s, s);
	return true;
}
bool BWE::StringToSize(BString& text, BSize& size)
{
	if (!text.isNumber())
	{
		return false;
	}
	if (text.contain(','))
	{
		BStringArray words;
		text.split(words, ',');
		if (words.size() != 2)
			return false;
		size.width() = words[0].toInt();
		size.height() = words[1].toInt();
		return true;
	}
	int s = text.toInt();
	size.set(s, s);
	return true;
}
bool BWE::StringToRect(const BString& text, BRect& rect)
{
	BStringArray words = text.split(',');
	if (words.size() == 4)
	{
		int x = words[0].toInt();
		int y = words[1].toInt();
		int w = words[2].toInt();
		int h = words[3].toInt();
		rect.set(x, y, w, h);
		return true;
	}
	return false;
}
bool BWE::StringToMargin(const BString& text, BMargin& margin)
{
	BStringArray words = text.split(',');
	if (words.size() == 4)
	{
		margin.left() = words[0].toInt();
		margin.top() = words[1].toInt();
		margin.right() = words[2].toInt();
		margin.bottom() = words[3].toInt();
		return true;
	}
	return false;
}
bool BWE::StringToBool(BString& text, bool& b)
{
	if (text == "true" || text == "on")
	{
		b = true;
		return true;
	}
	if (text == "false" || text == "off")
	{
		b = false;
		return true;
	}
	return false;
}

BHtmlItem::Display BWE::TextToDisplay(const BString& text)
{
	if (text == "inline-block")
		return BHtmlItem::Display_Inline_Block;
	else if (text == "block")
		return BHtmlItem::Display_Block;
	else if (text == "inline")
		return BHtmlItem::Display_Inline;
	else if (text == "flex")
		return BHtmlItem::Display_Flex;
	return BHtmlItem::Display_None;
}
BHtmlItem::Style BWE::TextToStyle(const BString& text)
{
	if (text == "none")
		return BHtmlItem::Style_None;
	else if (text == "disc")
		return BHtmlItem::Style_Disc;
	else if (text == "circle")
		return BHtmlItem::Style_Circle;
	else if (text == "square")
		return BHtmlItem::Style_Square;
	else if (text == "decimal")
		return BHtmlItem::Style_Decimal;
	else if (text == "decimal-leading-zero")
		return BHtmlItem::Style_DecimalLeadingZero;
	else if (text == "lower-roman")
		return BHtmlItem::Style_LowerRoman;
	else if (text == "upper-roman")
		return BHtmlItem::Style_UpperRoman;
	else if (text == "lower-alpha")
		return BHtmlItem::Style_LowerAlpha;
	else if (text == "upper-alpha")
		return BHtmlItem::Style_UpperAlpha;
	else if (text == "lower-greek")
		return BHtmlItem::Style_LowerGreek;
	else if (text == "lower-latin")
		return BHtmlItem::Style_LowerLatin;
	else if (text == "upper-latin")
		return BHtmlItem::Style_UpperLatin;
	else if (text == "hebrew")
		return BHtmlItem::Style_Hebrew;
	else if (text == "armenian")
		return BHtmlItem::Style_Armenian;
	else if (text == "georgian")
		return BHtmlItem::Style_Georgian;
	else if (text == "cjk-ideographic")
		return BHtmlItem::Style_CjkIdeographic;
	return BHtmlItem::Style_None;
}

State IHtmlStyleReader::StringToState(const char* cstr)
{
	if (!cstr)
		return State_None;
	if (0 == strcmp(cstr, "normal"))
		return State_Normal;
	if (0 == strcmp(cstr, "active") || 0 == strcmp(cstr, "actived"))
		return State_Actived;
	if (0 == strcmp(cstr, "hover") || 0 == strcmp(cstr, "hovered"))
		return State_Hovered;
	if (0 == strcmp(cstr, "press") || 0 == strcmp(cstr, "pressed"))
		return State_Pressed;
	if (0 == strcmp(cstr, "disable") || 0 == strcmp(cstr, "disabled"))
		return State_Disable;
	if (0 == strcmp(cstr, "select") || 0 == strcmp(cstr, "selected"))
		return State_Selected;
	return State_Max;
}
State IHtmlStyleReader::StringToState(const BString& str)
{
	if (str.empty())
		return State_None;
	if (str == "normal")
		return State_Normal;
	if (str == "active" || str == "actived")
		return State_Actived;
	if (str == "hover" || str == "hovered")
		return State_Hovered;
	if (str == "press" || str == "pressed")
		return State_Pressed;
	if (str == "disable" || str == "disabled")
		return State_Disable;
	if (str == "select" || str == "selected")
		return State_Selected;
	return State_Max;
}
bool IHtmlStyleReader::SplitImage(const BImage* image, BTable<BImageHolder>& imageTable)
{
	int row = imageTable.row();
	int column = imageTable.column();
	if (row < 1 || column < 1)
		return false;
	int width = image->width() / column;
	int height = image->height() / row;
	for (int r = 0; r < row; r++)
	{
		int y = r * height;
		for (int c = 0; c < column; c++)
		{
			int x = c * width;
			BImage* subimg = new BImage(width, height, image->format());
			image->clone(x, y, width, height, subimg);
			imageTable[r][c] = subimg;
		}
	}
	return true;
}

IHtmlStyleReader::IHtmlStyleReader()
{
	_lineNumber = 0;
	_linePos = 0;
}
IHtmlStyleReader::~IHtmlStyleReader()
{

}

bool IHtmlStyleReader::setPath(const BString& path)
{
	if (BEntry::Exist(path))
	{
		_path = path;
		return true;
	}
	return false;
}
const BString& IHtmlStyleReader::path() const
{
	return _path;
}

IHtmlStyle* IHtmlStyleReader::load(const BString& fileName)
{
	_message.clear();
	BString text;
	BFile::Load(fileName, text);
	_path = fileName.path();
	_lineNumber = 0;
	_linePos = 0;
	IHtmlStyle* style = new IHtmlStyle();
	int pos = 0;
	read(style, text, pos);
	if (pos < text.size())
		_message << "\tWarning : document stop at " << pos << '/' << text.size() << '\n';
	if (_message.size())
	{
		//this->emit(Signal_Error, _message);
	}
	return style;
}
bool IHtmlStyleReader::load(const BString& fileName, IHtmlStyle* style)
{
	_message.clear();
	BString text;
	BFile::Load(fileName, text);
	_path = fileName.path();
	_lineNumber = 0;
	_linePos = 0;
	int pos = 0;
	read(style, text, pos);
	if (pos < text.size())
		_message << "\tWarning : document stop at " << pos << '/' << text.size() << '\n';
	if (_message.size())
	{
		//this->emit(Signal_Error, _message);
		return false;
	}
	return true;
}

IHtmlStyle* IHtmlStyleReader::read(const BString& text)
{
	_message.clear();
	IHtmlStyle* style = new IHtmlStyle();
	_lineNumber = 0;
	_linePos = 0;
	int pos = 0;
	read(style, text, pos);
	if (pos < text.size())
		_message << "\tWarning : document stop at " << pos << '/' << text.size() << '\n';
	if (_message.size())
	{
		//this->emit(Signal_Error, message);
	}
	return style;
}
bool IHtmlStyleReader::read(const BString& text, IHtmlStyle* style)
{
	_message.clear();
	_lineNumber = 0;
	_linePos = 0;
	int pos = 0;
	read(style, text, pos);
	if (pos < text.size())
		_message << "\tWarning : document stop at " << pos << '/' << text.size() << '\n';
	if (_message.size())
	{
		//this->emit(Signal_Error, message);
		return false;
	}
	return true;
}
void IHtmlStyleReader::read(IHtmlStyle* style, const BString& text, int& pos)
{
	BString temp;
	BString comment;
	while (pos < text.size())
	{
		if (text[pos] == '\n' && pos > _linePos)
		{
			_lineNumber++;
			_linePos = pos;
		}
		if (text[pos] == '\r' || text[pos] == '\t' || text[pos] == '\n')
		{
			pos++;
			continue;
		}
		if (text[pos] == '/' && text(pos + 1) == '*')
		{
			readComment(text, ++pos, comment);
			continue;
		}
		char code = text[pos];
		if (code == '}')
		{
			pos++;
			break;
		}
		if (code == ';')
		{
			readDesc(temp, style);
			temp.reset();
			pos++;
			continue;
		}
		if (code == '{')
		{
			pos++;
			BString str;
			temp.trimmed();
			while (temp.contain(','))
			{
				int end = temp.find(',');
				temp.substr(str, 0, end);
				temp.remove(0, end + 1);
				IHtmlStyle* child = findChild(str, style);
				int begin = pos;
				read(child, text, pos);
				pos = begin;
			}
			if (temp == '*')
				temp.reset();
			IHtmlStyle* child = findChild(temp, style);
			read(child, text, pos);
			temp.reset();
			continue;
		}
		temp.append(code);
		pos++;
	}
	if (temp.size() > 1)
	{
		readDesc(temp, style);
		temp.reset();
	}
	_imageMap.clear();
	_iconMap.clear();
}

const BString& IHtmlStyleReader::message() const
{
	return _message;
}

bool IHtmlStyleReader::readComment(const BString& text, int& pos, BString& comment)
{
	comment.reset();
	if (text[pos] == '*')
	{
		while (++pos < text.size())
		{
			char c = text[pos];
			if (c == '\n' && pos > _linePos)
			{
				_lineNumber++;
				_linePos = pos;
			}
			else if (c == '*' && text(pos + 1) == '/')
			{
				pos += 2;
				return true;
			}
			comment << c;
		}
		return pos == text.size();
	}
	return false;
}
IHtmlStyle* IHtmlStyleReader::findChild(BString& text, IHtmlStyle* style)
{
	text.replace(' ', '>');
	int end = text.find('>');
	if (end < text.size())
	{
		BString str;
		text.substr(str, 0, end);
		IHtmlStyle* child = addStyle(str, style);
		int pos = end + 1;
		while (child && pos < text.size())
		{
			end = text.find('>', pos);
			text.substr(str, pos, end);
			child = addStyle(str, child);
			pos = end + 1;
		}
		return child;
	}
	IHtmlStyle* child = addStyle(text, style);
	return child;
}
IHtmlStyle* IHtmlStyleReader::addStyle(BString& text, IHtmlStyle* parent)
{
	text.simplify();
	text.trimmed();
	if (text.beginWith(':'))
	{
		IHtmlStyle* style = addSubStyle(text, 1, parent);
		return style;
	}
	int stop = text.find(':');
	BString name;
	BString id;
	BString cls;
	int id_pos = text.find('#', 0, stop);
	text.substr(name, 0, id_pos);
	name.simplify();
	name.trimmed();
	if (id_pos < stop)
	{
		text.substr(id, id_pos + 1, stop);
		id.simplify();
		id.trimmed();
	}
	int cls_pos = name.find('.');
	if (cls_pos < stop)
	{
		name.substr(cls, cls_pos + 1);
		name.remove(cls_pos, name.size());
	}
	IHtmlStyle* style = parent->child(name, id, cls);
	if (style == 0)
	{
		style = new IHtmlStyle();
		style->setName(name);
		style->id = id;
		style->cls = cls;
		parent->addChild(style);
	}
	if (stop < text.size())
	{
		style = addSubStyle(text, stop + 1, style);
	}
	return style;
}
IHtmlStyle* IHtmlStyleReader::addSubStyle(BString& text, int pos, IHtmlStyle* style)
{
	BString name;
	BString subHead;
	BString condition;
	int cond_begin = text.find('[');
	if (cond_begin < text.size())
	{
		int cond_end = text.find(']', cond_begin);
		text.substr(condition, cond_begin + 1, cond_end);
		text.remove(cond_begin, text.size());
	}
	int stop = text.find(':', pos);
	text.substr(name, pos, stop);
	name.simplify();
	name.trimmed();
	IHtmlStyle* annex = style->annex(name);
	if (annex == 0)
	{
		annex = new IHtmlStyle();
		annex->setName(name);
		style->addSub(annex);
	}
	if (stop < text.size())
	{
		annex = addSubStyle(text, stop + 1, annex);
	}
	return annex;
}

void IHtmlStyleReader::readDesc(BString& text, IHtmlStyle* style)
{
	text.simplify();
	text.trimmed();
	if (text.size())
	{
		if (text.beginWith("@import"))
		{
			text.remove("@import");
			text.trimmed();
			if (locateUrl(text))
			{
				IHtmlStyleReader reader;
				reader.load(text, style);
			}
			return;
		}
		BString sign;
		BString value;
		int sem_pos = text.find(':');
		if (sem_pos < text.size())
		{
			sign.reset();
			text.substr(sign, 0, sem_pos);
			sign.simplify();
			sign.trimmed();

			value.reset();
			text.substr(value, sem_pos + 1, text.size());
			value.simplify();
			value.trimmed();

			analysis(sign, value, style);
		}
		else
		{
			_message << "\tError[" << _lineNumber + 1 << "] : invalid desc : " << text << '\n';
		}
	}
}
bool IHtmlStyleReader::analysis(BString& sign, BString& value, IHtmlStyle* style)
{
	if (sign.beginWith("background"))
		return analysisBackground(style, sign, value);

	if (sign.beginWith("foreground"))
		return analysisForeground(style, sign, value);

	if (sign.beginWith("border"))
		return analysisBorder(style, sign, value);

	if (sign.beginWith("margin"))
		return analysisMargin(style, sign, value);

	if (sign.beginWith("padding"))
		return analysisPadding(style, sign, value);

	if (sign.beginWith("text"))
		return analysisText(style, sign, value);

	if (sign.beginWith("font"))
		return analysisFont(style, sign, value);

	if (sign.beginWith("title"))
		return analysisTitle(style, sign, value);

	if (sign.beginWith("image"))
		return analysisImage(style, sign, value);

	if (sign.beginWith("icon"))
		return analysisIcon(style, sign, value);

	if (sign.beginWith("groove"))
		return analysisGroove(style, sign, value);

	if (sign.beginWith("handle"))
		return analysisHandle(style, sign, value);

	if (sign.beginWith("separator"))
		return analysisSeparator(style, sign, value);

	if (sign.beginWith("arrow"))
		return analysisArrow(style, sign, value);

	if (sign.beginWith("grid"))
		return analysisGridLine(style, sign, value);

	if (sign.beginWith("graph"))
		return analysisGraph(style, sign, value);

	if (sign.beginWith("list-"))
		return analysisList(style, sign, value);

	if (sign == "width")
		return analysisWidth(style, sign, value);

	if (sign == "height")
		return analysisHeight(style, sign, value);

	if (sign == "min-width")
	{
		style->valueMap[sign] = value.toInt();
		return true;
	}

	if (sign == "max-width")
	{
		int maxWidth = value.toInt();
		style->valueMap[sign] = maxWidth;
		return true;
	}

	if (sign == "min-height")
	{
		int minHeight = value.toInt();
		style->valueMap[sign] = minHeight;
		return true;
	}

	if (sign == "max-height")
	{
		int maxHeight = value.toInt();
		style->valueMap[sign] = maxHeight;
		return true;
	}

	if (sign == "align")
	{
		Align align;
		if (StringToAlign(value, align))
			style->valueMap[sign] = align;
		return true;
	}

	if (sign == "space" || sign == "spacing")
	{
		int space = value.toInt();
		style->valueMap[sign] = space;
		return true;
	}

	if (sign == "color")
	{
		BColor color;
		if (StringToColor(value, color))
			style->valueMap[sign] = color;
		return true;
	}

	if (sign == "display")
	{
		style->valueMap[sign] = TextToDisplay(value);
		return true;
	}

	if (sign.beginWith("transition"))
		return analysisTransition(style, sign, value);

	if (sign.beginWith("flex"))
		return analysisFlex(style, sign, value);

	if (sign == "float")
	{
		Align floatAlign = Align_None;
		if (StringToAlign(value, floatAlign))
			style->valueMap[sign] = floatAlign;
		return true;
	}
	else if (sign == "vertical-align")
	{
		Align valign = Align_None;
		if (StringToAlign(value, valign))
			style->valueMap[sign] = valign;
		return true;
	}
	else if (sign == "offset")
	{
		BPoint offset;
		if (StringToPoint(value, offset))
			style->valueMap[sign] = offset;
		return true;
	}
	else if (sign == "prefix-color")
	{
		BColor color;
		if (StringToColor(value, color))
			style->valueMap["prefix"] = color;
		return true;
	}
	else if (sign == "suffix-color")
	{
		BColor color;
		if (StringToColor(value, color))
			style->valueMap["suffix"] = color;
		return true;
	}
	else if (sign == "flat")
	{
		bool flat = false;
		if (StringToBool(value, flat))
			style->valueMap[sign] = flat;
		return true;
	}
	else if (sign == "box-sizing")
	{
		style->valueMap[sign] = value;//content-box|border-box|inherit.
		return true;
	}
	else if (sign == "box-shadow")
	{
		style->valueMap[sign] = value;//box-shadow: h-shadow v-shadow blur spread color inset;
		return true;
	}
	else if (sign == "line-height")
	{
		float lineHeight = value.toFloat();
		style->valueMap["line-height"] = lineHeight;
		return true;
	}
	style->valueMap[sign] = value;
	return false;
}

bool IHtmlStyleReader::analysisTitle(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "title" || sign == "title-text")
	{
		const BString& title = value;
		style->valueMap[sign] = title;
		return true;
	}
	if (sign == "title-color")
	{
		BColor color;
		if (StringToColor(value, color))
		{
			style->valueMap["title"] = color;
		}
		return true;
	}
	if (sign == "title-image")
	{
		BImage* image = this->readImage(value);
		style->valueMap["title-image"] = image;
		if (!image)
		{
			_message << "\tError[" << _lineNumber + 1 << "] : " << sign << " : " << value << '\n';
		}
		return true;
	}
	if (sign == "title-icon")
	{
		BIcon* icon = this->readIcon(value);
		style->valueMap[Value_Icon] = icon;
		if (!icon)
		{
			_message << "\tError[" << _lineNumber + 1 << "] : " << sign << " : " << value << '\n';
		}
		return true;
	}
	return false;
}
bool IHtmlStyleReader::analysisText(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "text")
	{
		const BString& text = value;
		style->valueMap[sign] = text;
		return true;
	}
	if (sign == "text-align")
	{
		Align align;
		if (StringToAlign(value, align))
			style->valueMap[sign] = align;
		return true;
	}
	return false;
}
bool IHtmlStyleReader::analysisBackground(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "background")
	{
		BString colorText;
		BColor color;
		Align align;
		BStringArray words;
		int rgb_begin = value.find("rgb(");
		if (rgb_begin < value.size())
		{
			int rgb_end = value.find(')', rgb_begin);
			value.substr(colorText, rgb_begin, rgb_end + 1);
			value.remove(rgb_begin, rgb_end + 1);
		}
		int rgba_begin = value.find("rgba(");
		if (rgba_begin < value.size())
		{
			int rgba_end = value.find(')', rgba_begin);
			value.substr(colorText, rgba_begin, rgba_end + 1);
			value.remove(rgba_begin, rgba_end + 1);
		}
		value.split(words, ' ');
		if (colorText.size())
			words.append(colorText);
		while (words.size())
		{
			BString& text = words.first();
			if (text.beginWith("url"))
			{
				BImage* image = readImage(text);
				style->valueMap["background-image"] = image;
			}
			else if (StringToColor(text, color))
			{
				style->valueMap["background-color"] = color;
			}
			else if (StringToAlign(text, align))
			{
				style->valueMap["background-align"] = align;
			}
			else
			{
				_message << "\tError[" << _lineNumber + 1 << "] : " << sign << " : (" << text << ")invalid desc." << '\n';
			}
			words.remove(0);
		}
		if (words.size())
		{
			BString& imageText = words.first();
			words.remove(0);
		}
		return true;
	}
	if (sign == "background-image")
	{
		BImage* image = this->readImage(value);
		if (image)
		{
			style->valueMap[sign] = image;
			return true;
		}
		return false;
	}
	if (sign == "background-color")
	{
		BColor color;
		if (!StringToColor(value, color))
		{
			_message << "\tError[" << _lineNumber + 1 << "] : " << sign << " : invalid color desc." << '\n';
			return true;
		}
		style->valueMap[sign] = color;
		return true;
	}
	if (sign == "background-position")
	{
		Align align;
		BPoint point;
		if (StringToAlign(value, align))
		{
			style->valueMap["background-align"] = align;
		}
		else if (StringToPoint(value, point))
		{
			style->valueMap["background-position"] = point;
		}
		return true;
	}
	if (sign == "background-size")//background image size.
	{
		BSize size;
		if (StringToSize(value, size))
			style->valueMap[sign] = size;
		return true;
	}
	if (sign == "background-repeat")
	{
		style->valueMap[sign] = value;
		return true;
	}
	if (sign == "background-attachment")//background image scrolling is attaching page scrolling.
	{

	}
	if (sign == "background-blend-mode")//background image blend mode.
	{

	}
	if (sign == "background-clip")
	{

	}
	if (sign == "background-origin")//set background image origin.
	{

	}
	return false;
}
bool IHtmlStyleReader::analysisForeground(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign.beginWith("foreground-image"))
	{
		BImage* image = this->readImage(value);
		if (sign == "foreground-image")
		{
			style->valueMap[sign] = image;
			return true;
		}
		return true;
	}

	value.remove("-color");
	BColor color;
	if (!StringToColor(value, color))
	{
		_message << "\tError[" << _lineNumber + 1 << "] : " << sign << " : invalid color desc." << '\n';
		return true;
	}

	if (sign == "foreground")
	{
		style->valueMap["foreground-color"] = color;
		return true;
	}

	return false;
}
bool IHtmlStyleReader::analysisWidth(IHtmlStyle* style, BString& sign, BString& value)
{
	if (value.endWith('%'))
	{
		float ratio = value.toFloat() * 0.01f;
		style->valueMap[sign] = ratio;
		return true;
	}
	int width = value.toInt();
	style->valueMap[sign] = width;
	return true;
}
bool IHtmlStyleReader::analysisHeight(IHtmlStyle* style, BString& sign, BString& value)
{
	if (value.endWith('%'))
	{
		float ratio = value.toFloat() * 0.01f;
		style->valueMap[sign] = ratio;
		return true;
	}
	int height = value.toInt();
	style->valueMap[sign] = height;
	return true;
}
bool IHtmlStyleReader::analysisMargin(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "margin")
	{
		BStringArray words;
		value.split(words, ' ');
		if (words.size() == 1)
		{
			int perch = words[0].toInt();
			style->valueMap["margin-top"] = perch;
			style->valueMap["margin-right"] = perch;
			style->valueMap["margin-bottom"] = perch;
			style->valueMap["margin-left"] = perch;
			return true;
		}
		if (words.size() == 2)
		{
			int vperch = words[0].toInt();
			style->valueMap["margin-top"] = vperch;
			style->valueMap["margin-bottom"] = vperch;
			int hperch = words[1].toInt();
			style->valueMap["margin-left"] = hperch;
			style->valueMap["margin-right"] = hperch;
			return true;
		}
		if (words.size() == 3)
		{
			int topPerch = words[0].toInt();
			style->valueMap["margin-top"] = topPerch;
			int hperch = words[1].toInt();
			style->valueMap["margin-left"] = hperch;
			style->valueMap["margin-right"] = hperch;
			int bottomPerch = words[2].toInt();
			style->valueMap["margin-bottom"] = bottomPerch;
			return true;
		}
		if (words.size() == 4)
		{
			style->valueMap["margin-top"] = words[0].toInt();
			style->valueMap["margin-right"] = words[1].toInt();
			style->valueMap["margin-bottom"] = words[2].toInt();
			style->valueMap["margin-left"] = words[3].toInt();
			return true;
		}
		return true;
	}
	if (sign == "margin-left")
	{
		int left = value.toInt();
		style->valueMap[sign] = left;
		return true;
	}
	if (sign == "margin-top")
	{
		int top = value.toInt();
		style->valueMap[sign] = top;
		return true;
	}
	if (sign == "margin-right")
	{
		int right = value.toInt();
		style->valueMap[sign] = right;
		return true;
	}
	if (sign == "margin-bottom")
	{
		int bottom = value.toInt();
		style->valueMap[sign] = bottom;
		return true;
	}
	return false;
}
bool IHtmlStyleReader::analysisBorder(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "border")
	{
		int width = 0;
		BColor color;
		BString styleText;
		if (borderInfos(value, width, color, styleText))
		{
			style->valueMap["border-left-width"] = width;
			style->valueMap["border-left-color"] = color;
			style->valueMap["border-left-style"] = styleText;

			style->valueMap["border-top-width"] = width;
			style->valueMap["border-top-color"] = color;
			style->valueMap["border-top-style"] = styleText;

			style->valueMap["border-right-width"] = width;
			style->valueMap["border-right-color"] = color;
			style->valueMap["border-right-style"] = styleText;

			style->valueMap["border-bottom-width"] = width;
			style->valueMap["border-bottom-color"] = color;
			style->valueMap["border-bottom-style"] = styleText;
		}
		return true;
	}
	if (sign == "border-width")
	{
		int border = value.toInt();
		style->valueMap["border-left-width"] = border;
		style->valueMap["border-top-width"] = border;
		style->valueMap["border-right-width"] = border;
		style->valueMap["border-bottom-width"] = border;
		return true;
	}
	if (sign == "border-spacing")//set item spacing.
	{

	}
	if (sign == "border-style")//foure border styles
	{
		style->valueMap["border-left-style"] = value;
		style->valueMap["border-top-style"] = value;
		style->valueMap["border-right-style"] = value;
		style->valueMap["border-bottom-style"] = value;
		return true;
	}
	if (sign == "border-collapse")//collapse border as single.
	{

	}
	if (sign == "border-color")
	{
		BColor color;
		if (!StringToColor(value, color))
		{
			_message << "\tError[" << _lineNumber + 1 << "] : " << sign << " : invalid color desc." << '\n';
			return true;
		}
		style->valueMap["border-color"] = color;
		style->valueMap["border-left-color"] = color;
		style->valueMap["border-top-color"] = color;
		style->valueMap["border-right-color"] = color;
		style->valueMap["border-bottom-color"] = color;
		return true;
	}

	if (sign == "border-radius")
	{
		if (value.contain(' '))
		{
			BStringArray words;
			value.split(words, ' ');
			if (words.size() == 4)
			{
				style->valueMap["border-top-left-radius"] = BSize(words[0].toInt());
				style->valueMap["border-top-right-radius"] = BSize(words[1].toInt());
				style->valueMap["border-bottom-right-radius"] = BSize(words[2].toInt());
				style->valueMap["border-bottom-left-radius"] = BSize(words[3].toInt());
			}
			return true;
		}
		BSize radius;
		if (StringToSize(value, radius))
		{
			style->valueMap["border-top-left-radius"] = radius;
			style->valueMap["border-top-right-radius"] = radius;
			style->valueMap["border-bottom-right-radius"] = radius;
			style->valueMap["border-bottom-left-radius"] = radius;
		}
		return true;
	}
	if (sign == "border-top-left-radius")
	{
		BSize radius;
		if (StringToSize(value, radius))
			style->valueMap[sign] = radius;
		return true;
	}
	if (sign == "border-top-right-radius")
	{
		BSize radius;
		if (StringToSize(value, radius))
			style->valueMap[sign] = radius;
		return true;
	}
	if (sign == "border-bottom-right-radius")
	{
		BSize radius;
		if (StringToSize(value, radius))
			style->valueMap[sign] = radius;
		return true;
	}
	if (sign == "border-bottom-left-radius")
	{
		BSize radius;
		if (StringToSize(value, radius))
			style->valueMap[sign] = radius;
		return true;
	}

	if (sign == "border-image")
	{

	}
	if (sign == "border-image-outset")
	{

	}
	if (sign == "border-image-repeat")
	{

	}
	if (sign == "border-image-slice")
	{

	}
	if (sign == "border-image-source")
	{

	}
	if (sign == "border-image-width")
	{

	}

	if (sign == "border-left")
	{
		int width = 0;
		BColor color;
		BString styleText;
		if (borderInfos(value, width, color, styleText))
		{
			style->valueMap["border-left-width"] = width;
			style->valueMap["border-left-color"] = color;
			style->valueMap["border-left-style"] = styleText;
		}
		return true;
	}
	if (sign == "border-left-color")
	{
		BColor color;
		if (!StringToColor(value, color))
		{
			_message << "\tError[" << _lineNumber + 1 << "] : " << sign << " : invalid color desc." << '\n';
			return true;
		}
		style->valueMap["border-left-color"] = color;
		return true;
	}
	if (sign == "border-left-style")
	{

	}
	if (sign == "border-left-width")
	{

	}

	if (sign == "border-top")
	{
		int width = 0;
		BColor color;
		BString styleText;
		if (borderInfos(value, width, color, styleText))
		{
			style->valueMap["border-top-width"] = width;
			style->valueMap["border-top-color"] = color;
			style->valueMap["border-top-style"] = styleText;
		}
		return true;
	}
	if (sign == "border-top-color")
	{
		BColor color;
		if (!StringToColor(value, color))
		{
			_message << "\tError[" << _lineNumber + 1 << "] : " << sign << " : invalid color desc." << '\n';
			return true;
		}
		style->valueMap["border-top-color"] = color;
		return true;
	}
	if (sign == "border-top-style")
	{

	}
	if (sign == "border-top-width")
	{

	}

	if (sign == "border-right")
	{
		int width = 0;
		BColor color;
		BString styleText;
		if (borderInfos(value, width, color, styleText))
		{
			style->valueMap["border-right-width"] = width;
			style->valueMap["border-right-color"] = color;
			style->valueMap["border-right-style"] = styleText;
		}
		return true;
	}
	if (sign == "border-right-color")
	{
		BColor color;
		if (!StringToColor(value, color))
		{
			_message << "\tError[" << _lineNumber + 1 << "] : " << sign << " : invalid color desc." << '\n';
			return true;
		}
		style->valueMap["border-right-color"] = color;
		return true;
	}
	if (sign == "border-right-style")
	{

	}
	if (sign == "border-right-width")
	{

	}

	if (sign == "border-bottom")
	{
		int width = 0;
		BColor color;
		BString styleText;
		if (borderInfos(value, width, color, styleText))
		{
			style->valueMap["border-bottom-width"] = width;
			style->valueMap["border-bottom-color"] = color;
			style->valueMap["border-bottom-style"] = styleText;
		}
		return true;
	}
	if (sign == "border-bottom-color")
	{
		BColor color;
		if (!StringToColor(value, color))
		{
			_message << "\tError[" << _lineNumber + 1 << "] : " << sign << " : invalid color desc." << '\n';
			return true;
		}
		style->valueMap["border-bottom-color"] = color;
		return true;
	}
	if (sign == "border-bottom-style")
	{

	}
	if (sign == "border-bottom-width")
	{

	}

	return false;
}
bool IHtmlStyleReader::analysisPadding(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "padding")
	{
		BStringArray words;
		value.split(words, ' ');
		if (words.size() == 1)
		{
			int perch = words[0].toInt();
			if (value.endWith("rem"))
				perch *= 16;
			style->valueMap["padding-top"] = perch;
			style->valueMap["padding-right"] = perch;
			style->valueMap["padding-bottom"] = perch;
			style->valueMap["padding-left"] = perch;
			return true;
		}
		if (words.size() == 2)
		{
			int vperch = words[0].toInt();
			style->valueMap["padding-top"] = vperch;
			style->valueMap["padding-bottom"] = vperch;
			int hperch = words[1].toInt();
			style->valueMap["padding-left"] = hperch;
			style->valueMap["padding-right"] = hperch;
			return true;
		}
		if (words.size() == 3)
		{
			style->valueMap["padding-top"] = words[0].toInt();
			style->valueMap["padding-left"] = words[1].toInt();
			style->valueMap["padding-right"] = words[1].toInt();
			style->valueMap["padding-bottom"] = words[2].toInt();
			return true;
		}
		if (words.size() == 4)
		{
			style->valueMap["padding-top"] = words[0].toInt();
			style->valueMap["padding-right"] = words[1].toInt();
			style->valueMap["padding-bottom"] = words[2].toInt();
			style->valueMap["padding-left"] = words[3].toInt();
			return true;
		}
		return true;
	}
	if (sign == "padding-left")
	{
		int left = value.toInt();
		style->valueMap["padding-left"] = left;
		return true;
	}
	if (sign == "padding-right")
	{
		int right = value.toInt();
		style->valueMap["padding-right"] = right;
		return true;
	}
	if (sign == "padding-top")
	{
		int top = value.toInt();
		style->valueMap["padding-top"] = top;
		return true;
	}
	if (sign == "padding-bottom")
	{
		int bottom = value.toInt();
		style->valueMap["padding-bottom"] = bottom;
		return true;
	}
	if (sign.beginWith("padding-color"))
	{
		BColor color;
		if (!StringToColor(value, color))
		{
			_message << "\tError[" << _lineNumber + 1 << "] : " << sign << " : invalid color desc." << '\n';
			return true;
		}
		style->valueMap["padding-color"] = color;
		return true;
	}
	return false;
}
bool IHtmlStyleReader::analysisTransition(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "transition")
	{
		BStringArray words;
		value.split(words, ' ');
		if (words.size() >= 3)
		{
			style->valueMap["transition-property"] = words[0];
			style->valueMap["transition-duration"] = words[1].toReal();
			style->valueMap["transition-timing-function"] = words[2];
			//linear		==  cubic-bezier(0,0,1,1).
			//ease			== cubic-bezier(0.25,0.1,0.25,1).
			//ease-in		== cubic-bezier(0.42,0,1,1).
			//ease-out		== cubic-bezier(0,0,0.58,1).
			//ease-in-out	== cubic-bezier(0.42,0,0.58,1.
		}
		if (words.size() == 4)
		{
			style->valueMap["transition-delay"] = words[3].toReal();
		}
		return true;
	}
	if (sign == "transition-property")
	{
		style->valueMap["transition-property"] = value;
		return true;
	}
	if (sign == "transition-duration")
	{
		style->valueMap["transition-duration"] = value.toReal();
		return true;
	}
	if (sign == "transition-timing-function")
	{
		style->valueMap["transition-timing-function"] = value;
		return true;
	}
	if (sign == "transition-delay")
	{
		style->valueMap["transition-delay"] = value.toReal();
		return true;
	}
	return false;
}
bool IHtmlStyleReader::analysisList(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "list-style")
	{
		BStringArray words;
		value.split(words, ' ');
		if (words.size() > 0)
			style->valueMap["list-style-type"] = TextToStyle(words[0]);
		if (words.size() > 1)
			style->valueMap["list-style-position"] = words[1];
		if (words.size() > 2)
			style->valueMap["list-style-image"] = this->readImage(words[2]);
		return true;
	}
	if (sign == "list-style-type")
	{
		style->valueMap[sign] = TextToStyle(value);
		return true;
	}
	if (sign == "list-style-position")
	{
		style->valueMap[sign] = value;
		return true;
	}
	if (sign == "list-style-image")
	{
		style->valueMap[sign] = this->readImage(value);
		return true;
	}
	return false;
}
bool IHtmlStyleReader::analysisFont(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "font")
	{
		BStringArray words;
		if (value.split(words, ' ') < 2)
		{
			_message << "\tError[" << _lineNumber + 1 << "] : invalid font desc : " << value << '\n';
			return true;
		}
		BFontHolder font = new BFont();
		{//font-family.
			BString& familyText = words.last();
			if (familyText.contain(','))
			{
				BStringArray familyStrs;
				familyText.split(familyStrs, ',');
				for (int i = 0; i < familyStrs.size(); i++)
				{
					const BString& family = familyStrs[i];
					if (font->setFamily(family))
						break;
				}
			}
			else
			{
				font->setFamily(familyText);
			}
			if (font->empty())
			{
				_message << "\tError[" << _lineNumber + 1 << "] : font bad family : " << value << '\n';
				return true;
			}
			words.pop();
		}
		{//font-size.
			BString& sizeText = words.last();
			font->setSize(sizeText.toInt());
			int pos = sizeText.find('/');
			if (pos < sizeText.size())
			{
				sizeText.remove(0, pos + 1);
				float lineHeight = sizeText.toFloat();
				style->valueMap["line-height"] = lineHeight;
			}
			words.pop();
		}
		if (words.size())//try font style.
		{
			const BString& str = words.first();
			if (str == "normal" || str == "italic" || str == "oblique")
			{
				font->setProperty("font-style", str);
				words.remove(0);
			}
		}
		if (words.size())//try font weight.
		{
			const BString& str = words.first();
			if (str == "normal" || str == "bold" || str.isNumber())
			{
				font->setProperty("font-weight", str);
				words.remove(0);
			}
		}
		if (words.size())//try font variant.
		{
			const BString& str = words.first();
			if (str == "normal" || str == "small-caps")
			{
				font->setProperty("font-variant", str);
				words.remove(0);
			}
		}
		if (words.size())
		{
			_message << "\tError[" << _lineNumber + 1 << "] : font initialized failed : " << value << '\n';
			return true;
		}
		style->valueMap["font"] = font;
		return true;
	}
	if (sign == "font-family")
	{
		value.trimmed();
		if (value.empty())
			return true;
		BString family = findFamily(value);
		if (family.empty())
		{
			_message << "\tError[" << _lineNumber + 1 << "] : font initialized failed : " << value << '\n';
			return true;
		}
		BFontHolder font = new BFont();
		font->setFamily(family);
		style->valueMap["font"] = font;
		return true;
	}
	if (sign == "font-size")
	{
		if (value.endWith("em"))
			style->valueMap[sign] = value.toFloat();
		else if (value.endWith("%"))
			style->valueMap[sign] = value.toFloat() / 100;
		else
			style->valueMap[sign] = value.toInt();
		return true;
	}
	return false;
}
bool IHtmlStyleReader::analysisGroove(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "groove-size")
	{
		int size = value.toInt();
		style->valueMap[sign] = size;
		return true;
	}
	if (sign == "groove-color")
	{
		BColor color;
		if (!StringToColor(value, color))
		{
			_message << "\tError[" << _lineNumber + 1 << "] : " << sign << " : invalid color desc." << '\n';
			return true;
		}
		style->valueMap["groove-color"] = color;
		return true;
	}
	if (sign == "groove-image")
	{
		BImage* image = this->readImage(value);
		style->valueMap[sign] = image;
		return true;
	}
	return false;
}
bool IHtmlStyleReader::analysisGraph(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "graph")
	{
		Graph graph = Graph_None;
		if (value == "circle")
			graph = Graph_Circle;
		else if (value == "circle-fill")
			graph = Graph_Circle_Fill;
		else if (value == "square")
			graph = Graph_Square;
		else if (value == "square-fill")
			graph = Graph_Square_Fill;
		else if (value == "arrow-up")
			graph = Graph_Arrow_Up;
		else if (value == "arrow-up-fill")
			graph = Graph_Arrow_Up_Fill;
		else if (value == "arrow-down")
			graph = Graph_Arrow_Down;
		else if (value == "arrow-down-fill")
			graph = Graph_Arrow_Down_Fill;
		else if (value == "arrow-left")
			graph = Graph_Arrow_Left;
		else if (value == "arrow-left-fill")
			graph = Graph_Arrow_Left_Fill;
		else if (value == "arrow-right")
			graph = Graph_Arrow_Right;
		else if (value == "arrow-right-fill")
			graph = Graph_Arrow_Right_Fill;
		else if (value == "plus")
			graph = Graph_Plus;
		else if (value == "minus")
			graph = Graph_Minus;
		else if (value == "cross")
			graph = Graph_Cross;
		else if (value == "maximize")
			graph = Graph_Maximize;
		else if (value == "minimize")
			graph = Graph_Minimize;
		style->valueMap[sign] = graph;
		return true;
	}
	return false;
}
bool IHtmlStyleReader::analysisIcon(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "icon")
	{
		BIconHolder icon = this->readIcon(value);
		style->valueMap[sign] = icon;
		return true;
	}
	if (sign == "icon-size")
	{
		int w = value.toInt();
		BSize size(w, w);
		int comma_pos = value.find(',');
		value.remove(0, comma_pos + 1);
		value.trimmed();
		if (value.size())
		{
			size.height() = value.toInt();
		}
		style->valueMap[sign] = size;
		return true;
	}
	if (sign == "icon-expand")
	{
		BIconHolder icon = this->readIcon(value);
		style->valueMap[Icon_Expand] = icon;
		return true;
	}
	if (sign == "icon-shrink")
	{
		BIconHolder icon = this->readIcon(value);
		style->valueMap[Icon_Shrink] = icon;
		return true;
	}
	return false;
}
bool IHtmlStyleReader::analysisImage(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "image")
	{
		BImage* image = this->readImage(value);
		style->valueMap[sign] = image;
		return true;
	}
	if (sign == "image-size")
	{
		int w = value.toInt();
		BSize size(w, w);
		int comma_pos = value.find(',');
		value.remove(0, comma_pos + 1);
		value.trimmed();
		if (value.size())
		{
			size.height() = value.toInt();
		}
		style->valueMap[sign] = size;
		return true;
	}
	return false;
}
bool IHtmlStyleReader::analysisArrow(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "arrow-size")
	{
		BSize size;
		if (StringToSize(value, size))
			style->valueMap[sign] = size;
		return true;
	}

	if (sign == "arrow-align")
	{
		Align align = Align_None;
		if (StringToAlign(value, align))
			style->valueMap[sign] = align;
		return true;
	}

	if (sign.beginWith("arrow-color"))
	{
		BColor color;
		if (!StringToColor(value, color))
		{
			_message << "\tError[" << _lineNumber + 1 << "] : " << sign << " : invalid color desc." << '\n';
			return true;
		}
		if (sign == "arrow-color")
		{
			style->valueMap["Arrow"] = color;
			return true;
		}
		return true;
	}
	if (sign.beginWith("arrow-icon"))
	{
		BIcon* icon = this->readIcon(value);
		if (!icon)
		{
			_message << "\tError[" << _lineNumber + 1 << "] : arrow icon " << sign << " lost." << '\n';
			return true;
		}
		if (sign == "arrow-icon")
		{
			style->valueMap[Icon_Arrow_Up] = icon;
			style->valueMap[Icon_Arrow_Down] = icon;
			style->valueMap[Icon_Arrow_Left] = icon;
			style->valueMap[Icon_Arrow_Right] = icon;
			return true;
		}
		if (sign == "arrow-icon-up" || sign == "arrow-up-icon")
		{
			style->valueMap[Icon_Arrow_Up] = icon;
			return true;
		}
		if (sign == "arrow-icon-down" || sign == "arrow-down-icon")
		{
			style->valueMap[Icon_Arrow_Down] = icon;
			return true;
		}
		if (sign == "arrow-icon-left" || sign == "arrow-left-icon")
		{
			style->valueMap[Icon_Arrow_Left] = icon;
			return true;
		}
		if (sign == "arrow-icon-right" || sign == "arrow-right-icon")
		{
			style->valueMap[Icon_Arrow_Right] = icon;
			return true;
		}
		return true;
	}
	return false;
}
bool IHtmlStyleReader::analysisHandle(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "handle-size")
	{
		BSize size;
		if (StringToSize(value, size))
			style->valueMap[sign] = size;
		return true;
	}
	if (sign.beginWith("handle-color"))
	{
		BColor color;
		if (!StringToColor(value, color))
		{
			_message << "\tError[" << _lineNumber + 1 << "] : " << sign << " : invalid color desc." << '\n';
			return true;
		}
		if (sign == "handle-color")
		{
			style->valueMap[sign] = color;
			return true;
		}
	}
	return false;
}
bool IHtmlStyleReader::analysisSeparator(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "separator-color")
	{
		BColor color;
		if (StringToColor(value, color))
		{
			style->valueMap["Separator"] = color;
		}
		return true;
	}
	if (sign == "separator-image")
	{
		BImage* image = this->readImage(value);
		style->valueMap[sign] = image;
		return true;
	}
	if (sign == "separator-size")
	{
		BSize size;
		if (StringToSize(value, size))
			style->valueMap[sign] = size;
		return true;
	}
	if (sign == "indicator-size")
	{
		BSize size;
		if (StringToSize(value, size))
			style->valueMap[sign] = size;
		return true;
	}
	return false;
}
bool IHtmlStyleReader::analysisGridLine(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "grid-style")
	{
		GridStyle gridStyle = Grid_None;
		if (value == "vertical")
			gridStyle = Grid_Vertical;
		if (value == "horizontal")
			gridStyle = Grid_Horizontal;
		if (value == "crossing")
			gridStyle = Grid_Crossing;
		style->valueMap[sign] = gridStyle;
		return true;
	}
	if (sign == "grid-size" || sign == "grid-width")
	{
		int size = value.toInt();
		style->valueMap[sign] = size;
		return true;
	}
	if (sign == "grid-color")
	{
		BColor color;
		if (StringToColor(value, color))
		{
			style->valueMap["Grid_Hor"] = color;
			style->valueMap["Grid_Ver"] = color;
		}
		return true;
	}
	if (sign == "grid-color-ver" || sign == "grid-ver-color")
	{
		BColor color;
		if (StringToColor(value, color))
		{
			style->valueMap["Grid_Ver"] = color;
		}
		return true;
	}
	if (sign == "grid-color-hor" || sign == "grid-hor-color")
	{
		BColor color;
		if (StringToColor(value, color))
		{
			style->valueMap["Grid_Hor"] = color;
		}
		return true;
	}
	return false;
}
bool IHtmlStyleReader::analysisFlex(IHtmlStyle* style, BString& sign, BString& value)
{
	if (sign == "flex")
	{
		GridStyle gridStyle = Grid_None;
		if (value == "vertical")
			gridStyle = Grid_Vertical;
		if (value == "horizontal")
			gridStyle = Grid_Horizontal;
		if (value == "crossing")
			gridStyle = Grid_Crossing;
		style->valueMap[sign] = gridStyle;
		return true;
	}
	if (sign == "flex-basis")
	{
		return true;
	}
	if (sign == "flex-direction")
	{
		if (value == "row")
			style->valueMap[sign] = Orientation_Horizontal;
		else if (value == "row-reverse")
			style->valueMap[sign] = Orientation_Horizontal | Orientation_Reverse;
		else if (value == "column")
			style->valueMap[sign] = Orientation_Vertical;
		else if (value == "column-reverse")
			style->valueMap[sign] = Orientation_Vertical | Orientation_Reverse;
		else
			style->valueMap[sign] = Orientation_None;
		return true;
	}
	if (sign == "flex-flow")
	{
		return true;
	}
	if (sign == "flex-grow")
	{
		return true;
	}
	if (sign == "flex-shrink")
	{
		return true;
	}
	if (sign == "flex-wrap")
	{
		return true;
	}
	return false;
}

bool IHtmlStyleReader::locateUrl(BString& url)
{
	if (url.beginWith("url("))
	{
		url -= ')';
		url.remove("url(");
		if (url.beginWith('\'') && url.endWith('\''))
		{
			url.remove(0);
			url -= '\'';
		}
		if (url.beginWith('\"') && url.endWith('\"'))
		{
			url.remove(0);
			url -= '\"';
		}
	}
	if (BFile::Exist(url))
		return true;
	url.prepend('/');
	url.prepend(_path);
	if (BFile::Exist(url))
		return true;
	return false;
}
BImage* IHtmlStyleReader::readImage(BString& fileName)
{
	if (fileName == "none")
		return 0;
	if (fileName.beginWith("url("))
	{
		fileName -= ')';
		fileName.remove("url(");
		if (fileName.beginWith('\'') && fileName.endWith('\''))
		{
			fileName.remove(0);
			fileName -= '\'';
		}
		if (fileName.beginWith('\"') && fileName.endWith('\"'))
		{
			fileName.remove(0);
			fileName -= '\'';
		}
	}
	BImage* image = _imageMap(fileName);
	if (!image && _path.size())
	{
		BString filePath;
		filePath << _path << '/' << fileName;
		if (BFile::Exist(filePath))
			image = ReadFile<BImage>(filePath);
	}
	if (!image)
	{
		image = ReadFile<BImage>(fileName);
	}
	if (!image)
	{
		_message << "\tError[" << _lineNumber + 1 << "] : can't read image : " << fileName << '\n';
	}
	_imageMap[fileName] = image;
	return image;
}
BIcon* IHtmlStyleReader::readIcon(BString& fileName)
{
	int split = fileName.find('|');
	if (split < fileName.size())
	{
		BString temp;
		fileName.substr(temp, 0, split);
		temp.trimmed();
		BImage* image = readImage(temp);
		if (!image)
			return 0;

		BString splitText;
		fileName.substr(splitText, split + 1);
		int end = splitText.find(',');
		splitText.substr(temp, 0, end);

		int row = 0;
		int column = 0;
		BStringArray words;
		if (temp.contain('*'))
		{
			temp.split(words, '*');
			if (words.size() != 2)
			{
				_message << "\tError[" << _lineNumber + 1 << "] : can't load icon (first split info is invalid) : " << fileName << '\n';
				return 0;
			}
			row = words[0].toInt();
			column = words[1].toInt();
		}
		else
		{
			row = 1;
			column = temp.toInt();
		}

		splitText.truncate(end + 1);
		splitText.split(words, ',');
		if (words.size() > row * column)
		{
			_message << "\tError[" << _lineNumber + 1 << "] : can't load icon (too many states) : " << fileName << '\n';
			return 0;
		}

		BTable<BImageHolder> imageTable(row, column);
		SplitImage(image, imageTable);
		BIcon* icon = new BIcon();
		for (int i = 0; i < words.size(); i++)
		{
			words[i].trimmed();
			BImage* image = imageTable.at(i);
			State state = StringToState(words[i]);
			icon->setImage(image, state);
		}
		return icon;
	}
	if (fileName.ext())
	{
		if (BImage* image = readImage(fileName))
		{
			BIcon* icon = new BIcon(image);
			return icon;
		}
	}
	_message << "\tError[" << _lineNumber + 1 << "] : can't load icon : " << fileName << '\n';
	return 0;
}
BString IHtmlStyleReader::findFamily(BString& str)
{
	if (str.contain(','))
	{
		BStringArray familyStrs;
		str.split(familyStrs, ',');
		for (int i = 0; i < familyStrs.size(); i++)
		{
			BString& family = familyStrs[i];
			family.trimmed();
			if (family.beginWith('\''))
			{
				family.remove(0);
				family -= '\'';
			}
			const BStringArray& genericFamilies = IFontFamily::GenericFamily(family);
			for (int j = 0; j < genericFamilies.size(); j++)
			{
				const BString& family = genericFamilies[j];
				BString fileName = IFontFamily::SearchFileName(family);
				if (fileName.size())
					return family;
			}
			BString fileName = IFontFamily::SearchFileName(family);
			if (fileName.size())
				return family;
		}
		return BString();
	}
	return str;
}
